#include "w25qxx_cache.h"
#include "w25qxx_drive.h"

#define PAGE_SIZE               (256)
#define SECTOR_SIZE             (4096)

#define FLASH_GET_SECTOR(a)     ((a)/SECTOR_SIZE)
#define FLASH_GET_ADDR(s)

#define SECTOR_STATE_INIT     (0)
#define SECTOR_STATE_DIRTY    (0x12)

typedef struct
{
    uint8_t state;
    uint16_t sector;
    uint8_t data[SECTOR_SIZE];
}SECTOR_CACHE;
static SECTOR_CACHE _sector_cache;

static void _read_sector(uint16_t sector, uint8_t *data)
{
    uint8_t *ptdata = data;
    uint32_t address = sector*SECTOR_SIZE;

    W25QXX_Read(ptdata, address, SECTOR_SIZE);
}

uint32_t w25qxx_write_sector_count = 0;
static void _write_sector(uint16_t sector, uint8_t *data)
{
    uint8_t *ptdata = data;
    uint32_t address = sector*SECTOR_SIZE;
    uint8_t page_num = SECTOR_SIZE/PAGE_SIZE;

    W25QXX_Erase_Sector(sector);
    for(int i = 0; i < page_num; i++)
    {
        W25QXX_Write_Page(ptdata, address, PAGE_SIZE);
        address += PAGE_SIZE;
        ptdata += PAGE_SIZE;
    }
    w25qxx_write_sector_count++;
}

static int _read_sector_cache(uint32_t address, uint8_t *buffer, uint32_t size)
{
    if(buffer == NULL)
        return -1;

    uint16_t read_sector = FLASH_GET_SECTOR(address);
    if(read_sector != FLASH_GET_SECTOR(address+size-1))
        return -3;

    if(read_sector != _sector_cache.sector)
    {
        if(_sector_cache.state == SECTOR_STATE_DIRTY)
        {
            _write_sector(_sector_cache.sector, _sector_cache.data);
        }
        _sector_cache.sector = read_sector;
        _sector_cache.state = SECTOR_STATE_INIT;
        _read_sector(_sector_cache.sector, _sector_cache.data);
    }
    uint16_t offset = address%SECTOR_SIZE;
    memcpy(buffer,&(_sector_cache.data[offset]),size);

    return 1;
}

static int _write_sector_cache(uint32_t address, uint8_t *buffer, uint32_t size)
{
    if(buffer == NULL)
        return -1;

    uint16_t write_sector = FLASH_GET_SECTOR(address);
    if(write_sector != FLASH_GET_SECTOR(address+size-1))
        return -3;

    if(write_sector != _sector_cache.sector)
    {
        if(_sector_cache.state == SECTOR_STATE_DIRTY)
        {
            _write_sector(_sector_cache.sector, _sector_cache.data);
        }
        _sector_cache.sector = write_sector;
        _sector_cache.state = SECTOR_STATE_INIT;
        _read_sector(_sector_cache.sector, _sector_cache.data);
    }
    uint16_t offset = address%SECTOR_SIZE;
    memcpy(&(_sector_cache.data[offset]),buffer,size);
    _sector_cache.state = SECTOR_STATE_DIRTY;

    return 1;
}

uint32_t w25qxx_read_cache(uint32_t address, uint8_t *buffer, uint32_t size)
{
    while(size > 0)
    {
        uint16_t remain = SECTOR_SIZE - address%SECTOR_SIZE;
        uint16_t count = (size<remain)?(size):(remain);

        _read_sector_cache(address,buffer,count);
        address += count;
        buffer += count;
        size -= count;
    }
    return size;
}

uint32_t w25qxx_write_cache(uint32_t address, uint8_t *buffer, uint32_t size)
{
    while(size > 0)
    {
        uint16_t remain = SECTOR_SIZE - address%SECTOR_SIZE;
        uint16_t count = (size<remain)?(size):(remain);

        _write_sector_cache(address,buffer,count);
        address += count;
        buffer += count;
        size -= count;
    }
    return size;
}

void w25qxx_erase(uint32_t address, uint32_t size)
{
    W25QXX_Erase(address, size);
}

int w25qxx_init(void)
{
    W25QXX_Init();
    _sector_cache.sector = 0xffff;
    _sector_cache.state = SECTOR_STATE_INIT;
    if((W25QXX_TYPE >= W25Q80)&&(W25QXX_TYPE <= W25Q256))
    {
        return 1;
    }
    return 0;
}

void w25qxx_flush(void)
{
    if(_sector_cache.state == SECTOR_STATE_DIRTY)
    {
        _write_sector(_sector_cache.sector, _sector_cache.data);
        _sector_cache.state = SECTOR_STATE_INIT;
    }
}

uint32_t w25qxx_size(void)
{
    return W25QXX_SIZE;
}


